package com.minispringmvc.servlet;

import com.minispringmvc.annotation.*;
import com.minispringmvc.controller.UserController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@WebServlet(name="dispatherServlet",urlPatterns ="/",loadOnStartup = 1,initParams = {@WebInitParam(name="base-package",value="com.minispringmvc")})
public class DispatherServlet extends HttpServlet {
    private String  basePackage="";

    private List<String> packageNames = new ArrayList<String>();
    private Map<String,Object> instanceMap = new HashMap<String,Object>();
    private Map<String,String> nameMap = new HashMap<String,String>();
    private Map<String, Method> urlMethodMap = new HashMap<String, Method>();
    private Map<Method,String> methodPackageMap = new HashMap<Method,String>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("aaa");
        basePackage = config.getInitParameter("ase-package");
        scanBasePackage(basePackage);
        try {
            instance(packageNames);
            springIOC();
            handlerUrlMethodMap();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

    }


    private void scanBasePackage(String basePackage){
        URL url = this.getClass().getClassLoader().getResource(basePackage.replace("\\.","/"));
        File basePackageFile = new File(url.getPath());
        File[] files = basePackageFile.listFiles();
        for(File file : files){
            if(file.isDirectory()){
                scanBasePackage(basePackage+"."+file.getName());
            }else if(file.isFile()){
                packageNames.add(basePackage+"."+file.getName().split("\\.")[0]);
            }
        }
    }

    private void instance(List<String> packageNames) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if(packageNames.size() < 1){
            return;
        }

        for(String str : packageNames){
            Class<?> aClass = Class.forName(str);
            if(aClass.isAnnotationPresent(Controller.class)){
                Controller controller = aClass.getAnnotation(Controller.class);
                String controllerName = controller.value();

                instanceMap.put(controllerName,aClass.newInstance());
                nameMap.put(str,controllerName);
                System.out.println("Controller: "+str+",value: "+controllerName);
            }else if(aClass.isAnnotationPresent(Service.class)){
                Service service = aClass.getAnnotation(Service.class);
                String serviceName = service.value();

                instanceMap.put(serviceName,aClass.newInstance());
                nameMap.put(str,serviceName);
                System.out.println("Service: "+str+",value: "+serviceName);

            }else if (aClass.isAnnotationPresent(Repository.class)){
                Repository repository = aClass.getAnnotation(Repository.class);
                String repositoryName = repository.value();

                instanceMap.put(repositoryName,aClass.newInstance());
                nameMap.put(str,repositoryName);
                System.out.println("Repository: "+str+",value: "+repositoryName);
            }
        }

    }


    private void springIOC() throws IllegalAccessException {
        for(Map.Entry<String,Object> entry : instanceMap.entrySet()){
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for(Field field : fields){
                if(field.isAnnotationPresent(Qualifier.class)){
                    String name = field.getAnnotation(Qualifier.class).value();
                    field.setAccessible(true);
                    field.set(entry.getValue(),instanceMap.get(name));
                }
            }
        }
    }


    private void handlerUrlMethodMap() throws ClassNotFoundException {
        if(packageNames.size() < 1){
            return;
        }

        for(String str : packageNames){
            Class<?> c = Class.forName(str);
            if(c.isAnnotationPresent(Controller.class)){
                Method[] methods = c.getMethods();
                StringBuffer baseUrl = new StringBuffer();
                if(c.isAnnotationPresent(RequestMapping.class)){
                    RequestMapping requestMapping = c.getAnnotation(RequestMapping.class);
                    baseUrl.append(requestMapping.value());
                }

                for(Method method : methods){
                    if(method.isAnnotationPresent(RequestMapping.class)){
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        baseUrl.append(requestMapping.value());

                        urlMethodMap.put(baseUrl.toString(),method);
                        methodPackageMap.put(method,str);

                    }
                }
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        String contextPath = req.getContextPath();
        String path = uri.replaceAll(contextPath, "");

        Method method = urlMethodMap.get(path);
        if(method != null){
            String packageName = methodPackageMap.get(method);
            String controllerName = nameMap.get(packageName);

            UserController userController = (UserController) instanceMap.get(controllerName);
            method.setAccessible(true);
            try {
                method.invoke(userController);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }


    }
}


